package com.iflytek.edu.db.core.parse;

import cn.hutool.core.lang.SimpleCache;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.util.JdbcConstants;
import com.iflytek.edu.db.core.context.AdapterContext;
import com.iflytek.edu.db.core.enums.AdapterType;
import com.iflytek.edu.db.core.strategy.Strategy;
import com.iflytek.edu.db.core.strategy.StrategyFactory;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Optional;

/**
 * @author linkai
 * @date 2022/1/12
 */
@Slf4j
public class SqlAdapter {
    private final DbType sourceType = JdbcConstants.MYSQL;
    private final DbType targetType;
    private final SqlAdapterConfig adapterConfig;
    private final SqlParser parser;
    private final SimpleCache<String, String> cache = new SimpleCache<>();

    public SqlAdapter(DbType type) {
        this(type, null);
    }

    public static SqlAdapter create(DbType type) {
        return new SqlAdapter(type);
    }

    public SqlAdapter(DbType type, List<Strategy> strategyList) {
        this(type, null, strategyList);
    }

    public SqlAdapter(DbType type, AdapterType adapterType, List<Strategy> strategyList) {
        this.targetType = type;
        this.adapterConfig = StrategyFactory.createConfig(type);
        this.adapterConfig.addStrategy(strategyList);
        this.parser = new SqlParser(adapterConfig);
        this.adapterConfig.setAdapterType(Optional.ofNullable(adapterType).orElse(AdapterType.adapter));
    }

    public String parse(String sql) {
        AdapterContext.init(sql, this.adapterConfig.getAdapterType());

        try {
            // 生成AST数
            List<SQLStatement> statementList = SQLUtils.parseStatements(sql, sourceType);
            // SQL适配
            statementList.forEach(parser::parse);
            return SQLUtils.toSQLString(statementList, targetType);
        } finally {
            if (AdapterContext.isSearch()) {
                log.info("adapter search info: {}", AdapterContext.getInfo());
            }
            AdapterContext.remove();
        }
    }

    public String cacheParse(String sql) {
        return cache.get(sql, () -> parse(sql));
    }

}
